home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / system / serialxx.zip / SERIAL.CPP < prev    next >
C/C++ Source or Header  |  1989-12-19  |  15KB  |  486 lines

  1. // Copyright Prototronics
  2. // Totem Lake P.O. 8117
  3. // Kirkland, Washington 98034
  4.  
  5. // Joe Huffman 
  6. // September 16, 1989
  7. // (206) 820-1972
  8.  
  9. #ifdef DEBUG
  10. static char __file__[] = __FILE__;
  11. #define assert(e) ((void)((e) || _assert(__file__,__LINE__)))
  12. extern void _assert(const char *,unsigned);
  13. #else
  14. #define assert(e) ((void)(e))
  15. #endif
  16.  
  17. #include <stdlib.h>
  18. #include <dos.h>
  19.  
  20. #include "serial.hpp"
  21. #include "mem.h"
  22.  
  23. // Serial port code.
  24.  
  25. #define NO_MEMORY 1         // Status bits.
  26. #define NO_INTERRUPT 2      // Also defined in asm code.  Must be the same.
  27. #define NO_PORT 4
  28.  
  29. #define COM1_ADDRESS (0x3f8)        // Port addresses.
  30. #define COM2_ADDRESS (0x2f8)
  31. #define COM1_INT_NUM (0xc)
  32. #define COM2_INT_NUM (0xb)
  33.  
  34. #define INTR_NUM (modem_port_addr == COM1_ADDRESS? COM1_INT_NUM: COM2_INT_NUM)
  35.  
  36. #define COM1_EOI_INDICATOR (4 | 0x60)
  37. #define COM2_EOI_INDICATOR (3 | 0x60)
  38.  
  39. extern "C" int serial_port_init (struct serial_port_buffer *p),
  40.                serial_port_term (struct serial_port_buffer *p);
  41.  
  42. /****************************************************************************
  43. Initialize the serial port, set up the interrupt service routine.
  44. September 16, 1989
  45. ****************************************************************************/
  46. serial::serial (serial_port_number port_num, unsigned int size_of_buffer,
  47.                 serial_baud baud_rate)
  48. {
  49.   status_val = 0;
  50.   port = port_num;
  51.  
  52.   switch (port)
  53.   {
  54.   case SERIAL_COM1:
  55.     old_buffer.com_port_addr = modem_port_addr = COM1_ADDRESS;
  56.     old_buffer.int_number = COM1_INT_NUM;
  57.     old_buffer.eoi_indicator = COM1_EOI_INDICATOR;
  58.     break;
  59.  
  60.   case SERIAL_COM2:
  61.     old_buffer.com_port_addr = modem_port_addr = COM2_ADDRESS;
  62.     old_buffer.int_number = COM2_INT_NUM;
  63.     old_buffer.eoi_indicator = COM2_EOI_INDICATOR;
  64.     break;
  65.  
  66.   default:
  67.     assert (0);
  68.   }
  69.  
  70.   old_buffer.buf_size = size_of_buffer;
  71.   old_buffer.send_bytes = old_buffer.receive_bytes = 0;
  72.   old_buffer.send_head_p = old_buffer.send_tail_p = old_buffer.send_buffer = 0;
  73.   old_buffer.receive_head_p = old_buffer.receive_tail_p = 0;
  74.   old_buffer.receive_buffer = 0;
  75.  
  76.   void *s_buf = malloc (size_of_buffer);
  77.   void *r_buf = malloc (size_of_buffer);
  78.  
  79.   if (!r_buf || !s_buf)
  80.   {
  81.     free (r_buf);
  82.     free (s_buf);
  83.     status_val |= NO_MEMORY;
  84.     return;
  85.   }
  86.  
  87.   old_buffer.send_head_p = old_buffer.send_tail_p = 
  88.     old_buffer.send_buffer = s_buf;
  89.   old_buffer.receive_head_p = old_buffer.receive_tail_p = r_buf;
  90.   old_buffer.receive_buffer = r_buf;
  91.  
  92.   port_init ();
  93.   baud (baud_rate);
  94. }
  95. /*************************************************************************** 
  96. Restore the previous interrupt, buffers, UART register values. 
  97. September 17, 1989
  98. ****************************************************************************/
  99. serial::~serial (void)
  100. {
  101.   if (status_val & (NO_MEMORY | NO_PORT))
  102.     return;
  103.  
  104.   port_term ();
  105.  
  106.   free ((void *)old_buffer.receive_buffer);
  107.   free ((void *)old_buffer.send_buffer);
  108. }
  109.  
  110.  
  111. #define I8259_COM1_MASK ((unsigned char)~(1 << 4))    /* IRQ4 */
  112. #define I8259_COM2_MASK ((unsigned char)~(1 << 3))    /* IRQ3 */
  113. #define I8259_END_OF_INTR_PORT 0x20
  114. #define I8259_MASK_PORT 0x21
  115.  
  116. /* Interrupt enable bits. */
  117. #define REC_DATA_INTR (1 << 0)
  118. #define TRAN_DATA_INTR (1 << 1)
  119. #define REC_STATUS_INTR (1 << 2)
  120. #define MODEM_STATUS_INTR (1 << 3)
  121.  
  122. /* Interrupt ID defines. */
  123. #define INTR_PENDING (1)      /* If this bit position is 0, then yes there */
  124.                               /* is an interrupt pending. */
  125. #define INTR_LINE_STATUS (6)  /* Overrun Error or Parity Error or */
  126.                               /* Framing Error. */
  127. #define INTR_REC_DATA (4)     /* Received data availiable. */
  128. #define INTR_TRAN_EMPTY (2)   /* Transmit holding register is empty. */
  129.  
  130. #define LINE_CTRL_DATA (3)  /* Char length == 8, 1 stop bit, no parity, */
  131.  
  132. #define REC_REG (modem_port_addr + 0)
  133. #define TRAN_REG (modem_port_addr + 0)
  134. #define INTR_ENABLE_REG (modem_port_addr + 1)
  135. #define INTR_IDENT_REG (modem_port_addr + 2)
  136. #define LINE_CONTROL_REG (modem_port_addr + 3)
  137. #define MODEM_CONTROL_REG (modem_port_addr + 4)
  138. #define LINE_STATUS_REG (modem_port_addr + 5)
  139. #define MODEM_STATUS_REG (modem_port_addr + 6)
  140. #define SCRATCH_REG (modem_port_addr + 7)
  141. #define DIVISOR_LS_REG (modem_port_addr + 0)
  142. #define DIVISOR_MS_REG (modem_port_addr + 1)
  143. #define DLAB (0x80)   /* Divisor Latch Access Bit */
  144.  
  145. #define MAGIC_BIT (8)   /* This is a bit for the modem Control Register */
  146.                         /* that is used by IBM to enable interrupts, it */
  147.                         /* is user defineable as far as the chip is */
  148.                         /* concerned. */
  149.  
  150. #define DTR_BIT (1)     /* When set this sets the DTR line to a logic TRUE. */
  151.  
  152. /**********************************************************************
  153. The serial port initialization routine.
  154. **********************************************************************/
  155. void serial::port_init (void)
  156. {
  157.   old_registers.line_cntr_reg = inp (LINE_CONTROL_REG);
  158.  
  159.   /* Test for the presence of a serial port. */
  160.   /* Bit 7 must be set to a 0 to gain access to the INTR_ENABLE_REG. */
  161.   /* Just some value... as long as bit 7 is 0 */
  162.   outp (LINE_CONTROL_REG, 0x55);
  163.  
  164.   old_registers.intr_enable_reg = inp (INTR_ENABLE_REG);
  165.   outp (INTR_ENABLE_REG, 0); /* Disable all the serial port interrupts. */
  166.  
  167.   if (inp (INTR_ENABLE_REG) != 0 || inp (LINE_CONTROL_REG) != 0x55)
  168.   {
  169.     // Restore the registers just in case.
  170.     outp (LINE_CONTROL_REG, old_registers.line_cntr_reg);
  171.     outp (INTR_ENABLE_REG, old_registers.intr_enable_reg);
  172.     status_val = NO_PORT;
  173.     return;
  174.   }
  175.  
  176.   old_registers.modem_reg = inp (MODEM_CONTROL_REG);
  177.  
  178.   /* Enable access to the divisor latch. */
  179.   outp (LINE_CONTROL_REG, old_registers.line_cntr_reg | DLAB);
  180.   old_registers.divisor = (int) inp (DIVISOR_LS_REG);
  181.   old_registers.divisor |= ((int) inp (DIVISOR_MS_REG)) << 8;
  182.   outp (LINE_CONTROL_REG, LINE_CTRL_DATA);
  183.  
  184.   unsigned char enable_mask;
  185.  
  186.   switch (port)
  187.   {
  188.   case SERIAL_COM1:
  189.     enable_mask = I8259_COM1_MASK;
  190.     break;
  191.  
  192.   case SERIAL_COM2:
  193.     enable_mask = I8259_COM2_MASK;
  194.     break;
  195.  
  196.   default:
  197.     assert (0);
  198.   }
  199.  
  200.   enable_mask &= old_registers.enable_8259_mask = inp (I8259_MASK_PORT);
  201.  
  202.   // Swap bufffers, the new one is 'old_buffer'.  The old one is 
  203.   // serial_buffers [port].  Its okay to swap because the serial port
  204.   // interrupts have been disabled.
  205.  
  206.   struct serial_port_buffer temp_buf, *buf_p = &serial_buffers[port];
  207.   temp_buf = *buf_p;
  208.   *buf_p = old_buffer;
  209.   old_buffer = temp_buf;
  210.  
  211.   status_val |= serial_port_init (&serial_buffers[port]);
  212.  
  213.   outp (LINE_STATUS_REG, 0);    // Clear all error flags.
  214.   outp (MODEM_CONTROL_REG, MAGIC_BIT | DTR_BIT);
  215.  
  216.   outp (INTR_ENABLE_REG, REC_DATA_INTR);  // Enable only receive interrupts.
  217.   outp (I8259_END_OF_INTR_PORT, serial_buffers[port].eoi_indicator);
  218.  
  219.   outp (I8259_MASK_PORT, enable_mask);
  220. }
  221. /****************************************************************************
  222. Return 0 if the everything is okay.  Return ASCII string of error message if
  223. there is a problem.  Like not enough memory for the buffers, what ever.
  224. September 17, 1989
  225. ****************************************************************************/
  226. const char *serial::status (void)
  227. {
  228.   char *ret_val = 0;
  229.  
  230.   if (status_val & NO_MEMORY)
  231.     ret_val = "No memory for buffers.";
  232.   else
  233.   {
  234.     if (status_val & NO_PORT)
  235.       ret_val = "No serial port hardware.";
  236.     else
  237.     {
  238.       if (status_val & NO_INTERRUPT)
  239.         ret_val = "Unable to install interrupt service routine.";
  240.     }
  241.   }
  242.  
  243.   return ret_val;
  244. }
  245.  
  246. /**********************************************************************
  247. The serial port termination routine.
  248. **********************************************************************/
  249. void serial::port_term (void)
  250. {
  251.   outp (I8259_MASK_PORT, old_registers.enable_8259_mask);
  252.  
  253.   outp (INTR_ENABLE_REG, 0); /* Disable all the serial port interrupts. */
  254.  
  255.   // Restore the old interrupt. */
  256.   serial_port_term (&serial_buffers [port]);
  257.  
  258.   // Swap buffers.
  259.   struct serial_port_buffer temp_buf, *buf_p = &serial_buffers[port];
  260.   temp_buf = *buf_p;
  261.   *buf_p = old_buffer;
  262.   old_buffer = temp_buf;
  263.  
  264.   outp (MODEM_CONTROL_REG, old_registers.modem_reg);
  265.  
  266.   /* Enable access to the divisor latch. */
  267.   outp (LINE_CONTROL_REG, old_registers.line_cntr_reg | DLAB);
  268.  
  269.   outp (DIVISOR_LS_REG, old_registers.divisor);
  270.   outp (DIVISOR_MS_REG, old_registers.divisor >> 8);
  271.  
  272.   outp (LINE_CONTROL_REG, old_registers.line_cntr_reg);
  273.   outp (MODEM_CONTROL_REG, old_registers.modem_reg);
  274.  
  275.   outp (LINE_STATUS_REG, 0);    /* Clear all error flags. */
  276.   outp (I8259_END_OF_INTR_PORT, old_buffer.eoi_indicator);
  277.   outp (INTR_ENABLE_REG, old_registers.intr_enable_reg);
  278. }
  279.  
  280. /****************************************************************************
  281. Output a byte to the serial port.  Returns the number bytes in the output 
  282. buffer after this byte is put in.
  283. September 16, 1989
  284. ****************************************************************************/
  285. unsigned int serial::put (unsigned char b)
  286. {
  287.   struct serial_port_buffer *p = &serial_buffers [port];
  288.  
  289.   // Wait for the buffer to have room.
  290.   while (send_bytes_waiting() >= p->buf_size)
  291.     ;
  292.  
  293.   *p->send_head_p++ = b;
  294.   p->send_bytes++;
  295.  
  296.   // Enable transmit interrupts.
  297.   outp (INTR_ENABLE_REG, inp(INTR_ENABLE_REG) | TRAN_DATA_INTR);
  298.  
  299.   if (p->send_head_p - p->send_buffer >= p->buf_size)
  300.     p->send_head_p = p->send_buffer;
  301.  
  302.   return p->send_bytes;
  303. }
  304. /****************************************************************************
  305. Input a byte from the serial port.  Returns the number of bytes in the input
  306. buffer after this byte is taken out.  Returns ~0 if the input buffer was empty.
  307. September 16, 1989
  308. ****************************************************************************/
  309. unsigned int serial::get (unsigned char &b)
  310. {
  311.   struct serial_port_buffer *p = &serial_buffers [port];
  312.  
  313.   if (p->receive_bytes == 0)
  314.     return ~0;
  315.  
  316.   b = *p->receive_tail_p++;
  317.   if (p->receive_tail_p - p->receive_buffer >= p->buf_size)
  318.     p->receive_tail_p = p->receive_buffer;
  319.  
  320.   return --(p->receive_bytes);
  321. }
  322.  
  323. /****************************************************************************
  324. Get the number of bytes remaining in the receive buffer.
  325. September 16, 1989
  326. ****************************************************************************/
  327. unsigned int serial::received_bytes_waiting(void)
  328. {
  329.   return serial_buffers[port].receive_bytes;
  330. }
  331.  
  332. /****************************************************************************
  333. Get the number of bytes remaining in the send buffer.
  334. September 16, 1989
  335. ****************************************************************************/
  336. unsigned int serial::send_bytes_waiting(void)
  337. {
  338.   return serial_buffers[port].send_bytes;
  339. }
  340.  
  341. /****************************************************************************
  342. Set the baud rate.
  343. September 16, 1989
  344. ****************************************************************************/
  345. void serial::baud (serial_baud b)
  346. {
  347.   unsigned char line_cntr_reg = inp (LINE_CONTROL_REG);
  348.  
  349.   /* Enable access to the divisor latch. */
  350.   outp (LINE_CONTROL_REG, line_cntr_reg | DLAB);
  351.  
  352.   outp (DIVISOR_LS_REG, b & 0xFF);
  353.   outp (DIVISOR_MS_REG, b >> 8);
  354.  
  355.   outp (LINE_CONTROL_REG, LINE_CTRL_DATA);
  356. }
  357.  
  358. /****************************************************************************
  359. Clear the receive buffer.  All data in the receive buffer is lost.
  360. September 21, 1989
  361. ****************************************************************************/
  362. void serial::receive_clear (void)
  363. {
  364.   unsigned char enable_reg = inp (INTR_ENABLE_REG);
  365.   outp (INTR_ENABLE_REG, 0); // Disable all the serial port interrupts.
  366.  
  367.   struct serial_port_buffer *p = &serial_buffers [port];
  368.   p->receive_bytes = 0;
  369.   p->receive_head_p = p->receive_tail_p = p->receive_buffer;
  370.   outp (INTR_ENABLE_REG, enable_reg); // Restore serial port interrupts.
  371. }
  372.  
  373. /****************************************************************************
  374. Clear the transmit buffer.  All data in the transmit buffer is lost.
  375. September 21, 1989
  376. ****************************************************************************/
  377. void serial::transmit_clear (void)
  378. {
  379.   unsigned char enable_reg = inp (INTR_ENABLE_REG);
  380.   outp (INTR_ENABLE_REG, 0); // Disable all the serial port interrupts.
  381.  
  382.   struct serial_port_buffer *p = &serial_buffers [port];
  383.   p->send_bytes = 0;
  384.   p->send_head_p = p->send_tail_p = p->send_buffer;
  385.   outp (INTR_ENABLE_REG, enable_reg); // Restore serial port interrupts.
  386. }
  387.  
  388.  
  389.  
  390.  
  391. #ifdef TEST
  392.  
  393. #include <stdio.h>
  394. #include <time.h>
  395. #include <conio.h>
  396. #include <ctype.h>
  397.  
  398. #define key_waiting() kbhit()
  399. #define key_input() getch()
  400.  
  401. #define ESC 27
  402.  
  403. int main (int argc, char *argv[])
  404. {
  405.   char echo;
  406.   int port_num = 0;
  407.  
  408.   do
  409.   {
  410.     fputs ("\nEcho received data to sender (Y | N) ?", stdout);
  411.     echo = toupper(key_input());
  412.     fputc (echo, stdout);
  413.     fflush(stdout);
  414.   } while (echo != 'Y' && echo != 'N');
  415.  
  416.   if (echo == 'N')
  417.     echo = 0;
  418.  
  419.   char buf[150];
  420.   do
  421.   {
  422.     fputs ("\nPort (1 | 2)? ", stdout);
  423.     fflush (stdout);
  424.  
  425.     port_num = key_input();
  426.     fputc (port_num, stdout);
  427.     fputc ('\n', stdout);
  428.     port_num -= '0';
  429.   } while (port_num != 1 && port_num != 2);
  430.  
  431.   fflush (stdout);
  432.   serial p (port_num == 1? SERIAL_COM1: SERIAL_COM2, 1000, SERIAL_9600);
  433.  
  434.   assert (p.status() == 0);
  435.  
  436.   int i, done = 0;
  437.  
  438.   for (i = 2000; i > 0; i--)
  439.     p.put ('a');
  440.  
  441.   do
  442.   {
  443.     unsigned char b;
  444.  
  445.     while (p.get (b) != ~0)
  446.     {
  447.       if (b == ESC)
  448.         done = 1;
  449.  
  450.       fputc (b, stdout);
  451.  
  452.       if (echo)   // Echo back to port?
  453.         p.put (b);
  454.  
  455.       fflush (stdout);
  456.     }
  457.  
  458.     if (key_waiting())
  459.     {
  460.       int key = key_input();
  461.       if (key == ESC)
  462.         done = 1;
  463.  
  464.       p.put (key);
  465.     }
  466.   } while (!done);
  467.  
  468.   while (p.send_bytes_waiting() != 0)
  469.     ;
  470.  
  471.   sleep (1);
  472.  
  473.   return 0;
  474. }
  475.  
  476. /****************************************************************************
  477. Usual _assert() (for c++ functions).  Report the error and exit.
  478. September 16, 1989
  479. ****************************************************************************/
  480. void _assert (const char *file, unsigned line)
  481. {
  482.   printf("\nAssertion failure:\nfile: %s, line: %u\n", file, line);
  483.   exit (1);
  484. }
  485. #endif  // TEST
  486.